using System;
using System.Collections.Generic;
using System.Text;

namespace GuavaData
{
    
    public enum ConditionType { EQ, NEQ, GT, GE, LT, LE, BETWEEN, CONTAINS, ISNULL, ISNOTNULL, ARB }
    
    public class DisplayCondition
    {
        string conditionString;
        public string ConditionString
        {
            get { return conditionString; }
            set { conditionString = value; }
        }

        ConditionType type;
        public ConditionType Type
        {
            get { return type; }
            set { type = value; }
        }

        public DisplayCondition(ConditionType ct, string display)
        {
            this.type = ct;
            this.conditionString = display;
        }

        public override string ToString()
        {
            return this.ConditionString;
        }
    }
    
    public abstract class Condition : Object
    {

        public static Condition NewCondition(ConditionType ct, List<object> args)
        {
            switch (ct)
            {
                case ConditionType.BETWEEN:
                    return new BETCond(args);
                case ConditionType.CONTAINS:
                    return new ContainsCond(args);
                case ConditionType.EQ:
                    return new EQCond(args);
                case ConditionType.GE:
                    return new GECond(args);
                case ConditionType.GT:
                    return new GTCond(args);
                case ConditionType.ISNOTNULL:
                    return new NotNullCond(args);
                case ConditionType.ISNULL:
                    return new NullCond(args);
                case ConditionType.LE:
                    return new LECond(args);
                case ConditionType.LT:
                    return new LTCond(args);
                case ConditionType.NEQ:
                    return new NEQCond(args);
                case ConditionType.ARB:
                    return new ARBCond(args);
                default:
                    return null;
            }
        }

        public static Condition NewCondition(ConditionType ct)
        {
            List<object> args = new List<object>();
            return NewCondition(ct, args);
        }

        public static Condition NewCondition(ConditionType ct, object arg)
        {
            List<object> args = new List<object>();
            args.Add(arg);
            return NewCondition(ct, args);
        }

        public static Condition NewCondition(ConditionType ct, object arg1, object arg2)
        {
            List<object> args = new List<object>();
            args.Add(arg1);
            args.Add(arg2);
            return NewCondition(ct, args);
        }

        public static int NumArgsForCond(ConditionType ct)
        {
            switch (ct)
            {
                case ConditionType.BETWEEN:
                    return 2;
                case ConditionType.CONTAINS:
                    return 1;
                case ConditionType.EQ:
                    return 1;
                case ConditionType.GE:
                    return 1;
                case ConditionType.GT:
                    return 1;
                case ConditionType.ISNOTNULL:
                    return 0;
                case ConditionType.ISNULL:
                    return 0;
                case ConditionType.LE:
                    return 1;
                case ConditionType.LT:
                    return 1;
                case ConditionType.NEQ:
                    return 1;
                case ConditionType.ARB:
                    return 1;
                default:
                    return -1;
            }
        }

        public static bool NeedsOrderedForCond(ConditionType ct)
        {
            switch (ct)
            {
                case ConditionType.BETWEEN:
                    return true;
                case ConditionType.CONTAINS:
                    return false;
                case ConditionType.EQ:
                    return false;
                case ConditionType.GE:
                    return true;
                case ConditionType.GT:
                    return true;
                case ConditionType.ISNOTNULL:
                    return false;
                case ConditionType.ISNULL:
                    return false;
                case ConditionType.LE:
                    return true;
                case ConditionType.LT:
                    return true;
                case ConditionType.NEQ:
                    return false;
                case ConditionType.ARB:
                    return false;
                default:
                    return false;
            }
        }

        public static bool NeedsContainForCond(ConditionType ct)
        {
            switch (ct)
            {
                case ConditionType.BETWEEN:
                    return false;
                case ConditionType.CONTAINS:
                    return true;
                case ConditionType.EQ:
                    return false;
                case ConditionType.GE:
                    return false;
                case ConditionType.GT:
                    return false;
                case ConditionType.ISNOTNULL:
                    return false;
                case ConditionType.ISNULL:
                    return false;
                case ConditionType.LE:
                    return false;
                case ConditionType.LT:
                    return false;
                case ConditionType.NEQ:
                    return false;
                case ConditionType.ARB:
                    return false;
                default:
                    return false;
            }
        }
        public static DisplayCondition[] ConditionList
        {
            get
            {
                List<DisplayCondition> dcl = new List<DisplayCondition>();
                dcl.Add(new DisplayCondition(ConditionType.BETWEEN, "Between"));
                dcl.Add(new DisplayCondition(ConditionType.CONTAINS, "Contains"));
                dcl.Add(new DisplayCondition(ConditionType.EQ, "=="));
                dcl.Add(new DisplayCondition(ConditionType.GE, ">="));
                dcl.Add(new DisplayCondition(ConditionType.GT, ">"));
                dcl.Add(new DisplayCondition(ConditionType.ISNOTNULL, "Is Not Null"));
                dcl.Add(new DisplayCondition(ConditionType.ISNULL, "Is Null"));
                dcl.Add(new DisplayCondition(ConditionType.LE, "<="));
                dcl.Add(new DisplayCondition(ConditionType.LT, "<"));
                dcl.Add(new DisplayCondition(ConditionType.NEQ, "<>"));
                return dcl.ToArray();
            }
        }
        public abstract ConditionType CondType { get; }
        public abstract int NumArgs { get; }
        public abstract List<object> Args { get; }
        public abstract bool NeedsOrdered { get; }
        public abstract bool NeedsContain { get; }
        public abstract bool Validate(object obj, Domain.Domain d);
    }

    public class ConditionException : Exception
    {
        public int ConditionArguments;
        
        public ConditionException(string m, int i):base(m)
        {
            ConditionArguments = i;
        }
    }

    class ColumnReference : Object
    {
        public string columnAlias;
        public ColumnReference(string columnAlias)
        {
            this.columnAlias = columnAlias;
        }
    }

    class EQCond : Condition
    {
        private object arg;

        public EQCond(List<object> args)
        {
            if (args.Count != NumArgs) throw new ConditionException 
                ("You have specified the wrong number of conditions (" + args.Count + ") for an equality predicate.", NumArgs);
            arg = args[0];
        }
        public override string ToString()
        {
            return "=" + Common.Wrap(arg);
        }
        public override int NumArgs
        {
            get
            {
                return 1;
            }
        }
        public override List<object> Args
        {
            get
            {
                List<object> lo = new List<object>(NumArgs);
                lo.Add(arg);
                return lo;
            }
        }
        public override ConditionType CondType
        {
            get { return ConditionType.EQ; }
        }
        public override bool Equals(object obj)
        {
            return (obj is EQCond) && ((obj as EQCond).arg.Equals(arg));
        }
        public override int GetHashCode()
        {
            return this.GetType().GetHashCode() + arg.GetHashCode();
        }
        public override bool Validate(object obj, Domain.Domain d)
        {
            switch (d.ToString())
            {
                case "TinyInteger":
                    return (byte)obj == (byte)arg;

                case "SmallInteger":
                    return (Int16)obj == (Int16)arg;

                case "Integer":
                    return (int)obj == (int)arg;

                case "BigInteger":
                    return (Int64)obj == (Int64)arg;

                case "String":
                case "Enumerated":
                    return ((String)obj).Equals((String)arg);

                case "Numeric":
                    return (decimal)obj == (decimal)arg;

                case "DateTime":
                    return ((DateTime)obj).Equals((DateTime)arg);

                case "UniqueIdentifier":
                    return ((Guid)obj).Equals((Guid)arg);

                case "Boolean":
                    return (bool)obj == (bool)arg;

                default:
                    return false;
            }
        }
        public override bool NeedsOrdered
        {
            get { return false; }
        }
        public override bool NeedsContain
        {
            get { return false; }
        }
    }

    class NEQCond : Condition
    {
        private object arg;

        public NEQCond(List<object> args)
        {
            if (args.Count != NumArgs) throw new ConditionException ("You have specified the wrong number of conditions (" + args.Count + ") for an inequality predicate.", NumArgs);
            arg = args[0];
        }

        public override string ToString()
        {
            return "<>" + Common.Wrap(arg);
        }

        public override int NumArgs
        {
            get
            {
                return 1;
            }
        }
        public override List<object> Args
        {
            get
            {
                List<object> lo = new List<object>(NumArgs);
                lo.Add(arg);
                return lo;
            }
        }
        public override ConditionType CondType
        {
            get { return ConditionType.NEQ; }
        }
        public override bool Equals(object obj)
        {
            return (obj is NEQCond) && ((obj as NEQCond).arg.Equals(arg));
        }
        public override int GetHashCode()
        {
            return this.GetType().GetHashCode() + arg.GetHashCode();
        }
        public override bool Validate(object obj, Domain.Domain d)
        {
            switch (d.ToString())
            {
                case "TinyInteger":
                    return (byte)obj != (byte)arg;

                case "SmallInteger":
                    return (Int16)obj != (Int16)arg;

                case "Integer":
                    return (int)obj != (int)arg;

                case "BigInteger":
                    return (Int64)obj != (Int64)arg;

                case "String":
                case "Enumerated":
                    return ! (((String)obj).Equals((String)arg));

                case "Numeric":
                    return (decimal)obj != (decimal)arg;

                case "DateTime":
                    return ! (((DateTime)obj).Equals((DateTime)arg));

                case "UniqueIdentifier":
                    return ! (((Guid)obj).Equals((Guid)arg));

                case "Boolean":
                    return (bool)obj != (bool)arg;

                default:
                    return false;
            }
        }
        public override bool NeedsOrdered
        {
            get { return false; }
        }
        public override bool NeedsContain
        {
            get { return false; }
        }
    }

    class GTCond : Condition
    {
        private object arg;

        public GTCond(List<object> args)
        {
            if (args.Count != NumArgs) throw new ConditionException ("You have specified the wrong number of conditions (" + args.Count + ") for a greater-than predicate.", NumArgs);
            arg = args[0];
        }
        public override string ToString()
        {
            return ">" + Common.Wrap(arg);
        }
        public override int NumArgs
        {
            get
            {
                return 1;
            }
        }
        public override List<object> Args
        {
            get
            {
                List<object> lo = new List<object>(NumArgs);
                lo.Add(arg);
                return lo;
            }
        }
        public override ConditionType CondType
        {
            get { return ConditionType.GT; }
        }
        public override bool Equals(object obj)
        {
            return (obj is GTCond) && ((obj as GTCond).arg.Equals(arg));
        }
        public override int GetHashCode()
        {
            return this.GetType().GetHashCode() + arg.GetHashCode();
        }
        public override bool Validate(object obj, Domain.Domain d)
        {
            switch (d.ToString())
            {
                case "TinyInteger":
                    return (byte)obj > (byte)arg;

                case "SmallInteger":
                    return (Int16)obj > (Int16)arg;

                case "Integer":
                    return (int)obj > (int)arg;

                case "BigInteger":
                    return (Int64)obj > (Int64)arg;

                case "String":
                    return ((String)obj).CompareTo((String)arg) > 0;

                case "Numeric":
                    return (decimal)obj > (decimal)arg;

                case "DateTime":
                    return ((DateTime)obj).CompareTo((DateTime)arg) > 0;

                default:
                    return false;
            }
        }
        public override bool NeedsOrdered
        {
            get { return true; }
        }
        public override bool NeedsContain
        {
            get { return false; }
        }
    }

    class GECond : Condition
    {
        private object arg;

        public GECond(List<object> args)
        {
            if (args.Count != NumArgs) throw new ConditionException ("You have specified the wrong number of conditions (" + args.Count + ") for a greater-or-equal predicate.", NumArgs);
            arg = args[0];
        }
        public override string ToString()
        {
            return ">=" + Common.Wrap(arg);
        }
        public override int NumArgs
        {
            get
            {
                return 1;
            }
        }
        public override List<object> Args
        {
            get
            {
                List<object> lo = new List<object>(NumArgs);
                lo.Add(arg);
                return lo;
            }
        }
        public override ConditionType CondType
        {
            get { return ConditionType.GE; }
        }
        public override bool Equals(object obj)
        {
            return (obj is GECond) && ((obj as GECond).arg.Equals(arg));
        }
        public override int GetHashCode()
        {
            return this.GetType().GetHashCode() + arg.GetHashCode();
        }
        public override bool Validate(object obj, Domain.Domain d)
        {
            switch (d.ToString())
            {
                case "TinyInteger":
                    return (byte)obj >= (byte)arg;

                case "SmallInteger":
                    return (Int16)obj >= (Int16)arg;

                case "Integer":
                case "IntegerInterval":
                    return (int)obj >= (int)arg;

                case "BigInteger":
                    return (Int64)obj >= (Int64)arg;

                case "String":
                    return ((String)obj).CompareTo((String)arg) >= 0;

                case "Numeric":
                    return (decimal)obj >= (decimal)arg;

                case "DateTime":
                    return ((DateTime)obj).CompareTo((DateTime)arg) >= 0;

                default:
                    return false;
            }
        }
        public override bool NeedsOrdered
        {
            get { return true; }
        }
        public override bool NeedsContain
        {
            get { return false; }
        }
    }

    class LTCond : Condition
    {
        private object arg;

        public LTCond(List<object> args)
        {
            if (args.Count != NumArgs) throw new ConditionException ("You have specified the wrong number of conditions (" + args.Count + ") for a less-than predicate.", NumArgs);
            arg = args[0];
        }
        public override string ToString()
        {
            return "<" + Common.Wrap(arg);
        }
        public override int NumArgs
        {
            get
            {
                return 1;
            }
        }
        public override List<object> Args
        {
            get
            {
                List<object> lo = new List<object>(NumArgs);
                lo.Add(arg);
                return lo;
            }
        }
        public override ConditionType CondType
        {
            get { return ConditionType.LT; }
        }
        public override bool Equals(object obj)
        {
            return (obj is LTCond) && ((obj as LTCond).arg.Equals(arg));
        }
        public override int GetHashCode()
        {
            return this.GetType().GetHashCode() + arg.GetHashCode();
        }
        public override bool Validate(object obj, Domain.Domain d)
        {
            switch (d.ToString())
            {
                case "TinyInteger":
                    //System.Windows.Forms.MessageBox.Show(obj.GetType().ToString());
                    //System.Windows.Forms.MessageBox.Show(arg.GetType().ToString());
                    return (byte)obj < (byte)arg;

                case "SmallInteger":
                    return (Int16)obj < (Int16)arg;

                case "Integer":
                case "IntegerInterval":
                    return (int)obj < (int)arg;

                case "BigInteger":
                    return (Int64)obj < (Int64)arg;

                case "String":
                    return ((String)obj).CompareTo((String)arg) < 0;

                case "Numeric":
                    return (decimal)obj < (decimal)arg;

                case "DateTime":
                    return ((DateTime)obj).CompareTo((DateTime)arg) < 0;

                default:
                    return false;
            }
        }
        public override bool NeedsOrdered
        {
            get { return true; }
        }
        public override bool NeedsContain
        {
            get { return false; }
        }
    }

    class LECond : Condition
    {
        private object arg;

        public LECond(List<object> args)
        {
            if (args.Count != NumArgs) throw new ConditionException ("You have specified the wrong number of conditions (" + args.Count + ") for a less-or-equal predicate.", NumArgs);
            arg = args[0];
        }
        public override string ToString()
        {
            return "<=" + Common.Wrap(arg);
        }
        public override int NumArgs
        {
            get
            {
                return 1;
            }
        }
        public override List<object> Args
        {
            get
            {
                List<object> lo = new List<object>(NumArgs);
                lo.Add(arg);
                return lo;
            }
        }
        public override ConditionType CondType
        {
            get { return ConditionType.LE; }
        }
        public override bool Equals(object obj)
        {
            return (obj is LECond) && ((obj as LECond).arg.Equals(arg));
        }
        public override int GetHashCode()
        {
            return this.GetType().GetHashCode() + arg.GetHashCode();
        }
        public override bool Validate(object obj, Domain.Domain d)
        {
            switch (d.ToString())
            {
                case "TinyInteger":
                    return (byte)obj <= (byte)arg;

                case "SmallInteger":
                    return (Int16)obj <= (Int16)arg;

                case "Integer":
                case "IntegerInterval":
                    return (int)obj <= (int)arg;

                case "BigInteger":
                    return (Int64)obj <= (Int64)arg;

                case "String":
                    return ((String)obj).CompareTo((String)arg) <= 0;

                case "Numeric":
                    return (decimal)obj <= (decimal)arg;

                case "DateTime":
                    return ((DateTime)obj).CompareTo((DateTime)arg) <= 0;

                default:
                    return false;
            }
        }
        public override bool NeedsOrdered
        {
            get { return true; }
        }
        public override bool NeedsContain
        {
            get { return false; }
        }
    }

    class BETCond : Condition
    {
        private object arg1;
        private object arg2;

        public BETCond(List<object> args)
        {
            if (args.Count != NumArgs) throw new ConditionException ("You have specified the wrong number of conditions (" + args.Count + ") for a between predicate.", NumArgs);
            arg1 = args[0];
            arg2 = args[1];
        }
        public override string ToString()
        {
            return " BETWEEN " + Common.Wrap(arg1) + " AND " + Common.Wrap(arg2);
        }
        public override int NumArgs
        {
            get
            {
                return 2;
            }
        }
        public override List<object> Args
        {
            get
            {
                List<object> lo = new List<object>(NumArgs);
                lo.Add(arg1);
                lo.Add(arg2);
                return lo;
            }
        }
        public override ConditionType CondType
        {
            get { return ConditionType.BETWEEN; }
        }
        public override bool Equals(object obj)
        {
            return (obj is BETCond) && ((obj as BETCond).arg1.Equals(arg1)) && ((obj as BETCond).arg2.Equals(arg2));
        }
        public override int GetHashCode()
        {
            return (this.GetType().GetHashCode() + arg1.GetHashCode()) * arg2.GetHashCode();
        }
        public override bool Validate(object obj, Domain.Domain d)
        {
            switch (d.ToString())
            {
                case "TinyInteger":
                    return ((byte)obj > (byte)arg1) && ((byte)obj < (byte)arg2);

                case "SmallInteger":
                    return ((Int16)obj > (Int16)arg1) && ((Int16)obj < (Int16)arg2);

                case "Integer":
                case "IntegerInterval":
                    return ((int)obj > (int)arg1) && ((int)obj < (int)arg2);

                case "BigInteger":
                    return ((Int64)obj > (Int64)arg1) && ((Int64)obj < (Int64)arg2);

                case "String":
                    return (((String)obj).CompareTo((String)arg1) > 0) && (((String)obj).CompareTo((String)arg2) < 0);

                case "Numeric":
                    return ((decimal)obj > (decimal)arg1) && ((decimal)obj < (decimal)arg2);

                case "DateTime":
                    return (((DateTime)obj).CompareTo((DateTime)arg1) > 0) && (((DateTime)obj).CompareTo((DateTime)arg2) < 0);

                default:
                    return false;
            }
        }
        public override bool NeedsOrdered
        {
            get { return true; }
        }
        public override bool NeedsContain
        {
            get { return false; }
        }
    }

    class ContainsCond : Condition
    {
        private object arg;

        public ContainsCond(List<object> args)
        {
            if (args.Count != NumArgs) throw new ConditionException ("You have specified the wrong number of conditions (" + args.Count + ") for a containment predicate.", NumArgs);
            arg = args[0];
        }
        public override string ToString()
        {
            return " LIKE " + Common.Wrap("%" + arg.ToString() + "%");
        }
        public override int NumArgs
        {
            get
            {
                return 1;
            }
        }
        public override List<object> Args
        {
            get
            {
                List<object> lo = new List<object>(NumArgs);
                lo.Add(arg);
                return lo;
            }
        }
        public override ConditionType CondType
        {
            get { return ConditionType.CONTAINS; }
        }
        public override bool Equals(object obj)
        {
            return (obj is ContainsCond) && ((obj as ContainsCond).arg.Equals(arg));
        }
        public override int GetHashCode()
        {
            return this.GetType().GetHashCode() + arg.GetHashCode();
        }
        public override bool Validate(object obj, Domain.Domain d)
        {
            if (d.ToString() == "String") return (obj as String).Contains(arg as String);
            return false;
        }
        public override bool NeedsOrdered
        {
            get { return false; }
        }
        public override bool NeedsContain
        {
            get { return true; }
        }
    }

    class NullCond : Condition
    {
        public NullCond(List<object> args)
        {
            if (args.Count != NumArgs) throw new ConditionException ("You have specified the wrong number of conditions (" + args.Count + ") for a null predicate.", NumArgs);
        }
        public override string ToString()
        {
            return " IS NULL";
        }
        public override int NumArgs
        {
            get
            {
                return 0;
            }
        }
        public override List<object> Args
        {
            get
            {
                return new List<object>();
            }
        }
        public override ConditionType CondType
        {
            get { return ConditionType.ISNULL; }
        }
        public override bool Equals(object obj)
        {
            return (obj is NullCond);
        }
        public override int GetHashCode()
        {
            return this.GetType().GetHashCode();
        }
        public override bool Validate(object obj, Domain.Domain d)
        {
            return ((obj == null) || (obj is DBNull));
        }
        public override bool NeedsOrdered
        {
            get { return false; }
        }
        public override bool NeedsContain
        {
            get { return false; }
        }
    }

    class NotNullCond : Condition
    {
        public NotNullCond(List<object> args)
        {
            if (args.Count != NumArgs) throw new ConditionException ("You have specified the wrong number of conditions (" + args.Count + ") for an equality predicate.", NumArgs);
        }
        public override string ToString()
        {
            return " IS NOT NULL";
        }
        public override int NumArgs
        {
            get
            {
                return 0;
            }
        }
        public override List<object> Args
        {
            get
            {
                return new List<object>();
            }
        }
        public override ConditionType CondType
        {
            get { return ConditionType.ISNOTNULL; }
        }
        public override bool Equals(object obj)
        {
            return (obj is NotNullCond);
        }
        public override int GetHashCode()
        {
            return this.GetType().GetHashCode();
        }
        public override bool Validate(object obj, Domain.Domain d)
        {
            return ((obj != null) && ! (obj is DBNull));
        }
        public override bool NeedsOrdered
        {
            get { return false; }
        }
        public override bool NeedsContain
        {
            get { return false; }
        }
    }

    class ARBCond : Condition
    {
        private object arg1;


        public ARBCond(List<object> args)
        {
            if (args.Count != NumArgs) throw new ConditionException("You have specified the wrong number of conditions (" + args.Count + ") for a ARB predicate.", NumArgs);
            arg1 = args[0];

        }
        public override string ToString()
        {
            return (String) arg1;
        }
        public override int NumArgs
        {
            get
            {
                return 1;
            }
        }
        public override List<object> Args
        {
            get
            {
                List<object> lo = new List<object>(NumArgs);
                lo.Add(arg1);

                return lo;
            }
        }
        public override ConditionType CondType
        {
            get { return ConditionType.ARB; }
        }
        public override bool Equals(object obj)
        {
            return (obj is ARBCond) && ((obj as ARBCond).arg1.Equals(arg1)) ;
        }
        public override int GetHashCode()
        {
            return (this.GetType().GetHashCode() + arg1.GetHashCode());
        }
        public override bool Validate(object obj, Domain.Domain d)
        {
            switch (d.ToString())
            {
                case "TinyInteger":
                    return ((byte)obj > (byte)arg1);

                case "SmallInteger":
                    return ((Int16)obj > (Int16)arg1) ;

                case "Integer":
                case "IntegerInterval":
                    return ((int)obj > (int)arg1) ;

                case "BigInteger":
                    return ((Int64)obj > (Int64)arg1) ;

                case "String":
                    return (((String)obj).CompareTo((String)arg1) > 0);

                case "Numeric":
                    return ((decimal)obj > (decimal)arg1) ;

                case "DateTime":
                    return (((DateTime)obj).CompareTo((DateTime)arg1) > 0);

                default:
                    return false;
            }
        }
        public override bool NeedsOrdered
        {
            get { return true; }
        }
        public override bool NeedsContain
        {
            get { return false; }
        }
    }
}
